{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import matplotlib.pyplot as plt\n",
    "from torch.autograd import Variable\n",
    "import torch.nn.functional\t\n",
    "import seaborn as sns\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch.nn.functional as F"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "读取数据----------------------------------------------------\n",
      "(602, 4)\n",
      "(tensor([[0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0.]]), tensor(0.))\n"
     ]
    }
   ],
   "source": [
    "data_dim = int(3) \n",
    "\n",
    "\n",
    "\n",
    "def get_data():\n",
    "    # 读取数据\n",
    "    print('读取数据----------------------------------------------------')\n",
    "    dataframe = pd.read_csv(r'意大利.csv', header=0, index_col=0)\n",
    "    dataset = dataframe.values\n",
    "    # print(dataset)\n",
    "    print(dataset.shape)\n",
    "    # print('归一化操作----------------------------------------------------')\n",
    "    # scaler = MinMaxScaler(feature_range=(0, 1))\n",
    "    #将数据集转换为tensor，因为PyTorch模型是使用tensor进行训练的，并将训练数据转换为输入序列和相应的标签\n",
    "    # print(dataset)\n",
    "    train = torch.FloatTensor(dataset)\n",
    "    # train = scaler.fit_transform(train)\n",
    "    # print(train)\n",
    "    # train = dataset\n",
    "    x_data = []\n",
    "    y_data = []\n",
    "    input_seq=[]\n",
    "\n",
    "\n",
    "    for i in range(data_dim, len(train)):\n",
    "        # 计算过去点的梯度数目和历史的数据\n",
    "        x_data=train[i - data_dim: i]\n",
    "        y_data=train[i][0] # 目标\n",
    "        input_seq.append((x_data, y_data))#inout_seq内的数据不断更新，但是总量只有tw+1个\n",
    "    return input_seq\n",
    "input_seq=get_data()\n",
    "print(input_seq[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\"\"\"\n",
    "创建LSTM模型\n",
    "参数说明：\n",
    "1、input_size:对应的及特征数量，此案例中为1，即passengers\n",
    "2、output_size:预测变量的个数，及数据标签的个数\n",
    "2、hidden_layer_size:隐藏层的特征数，也就是隐藏层的神经元个数\n",
    "\"\"\"\n",
    "class LSTM1(nn.Module):#注意Module首字母需要大写\n",
    "    def __init__(self, input_size=4, hidden_layer_size=100, output_size=1):\n",
    "        super().__init__()\n",
    "        self.hidden_layer_size = hidden_layer_size\n",
    "\n",
    "        # 创建LSTM层和linear层，LSTM层提取特征，linear层用作最后的预测\n",
    "        ##LSTM算法接受三个输入：先前的隐藏状态，先前的单元状态和当前输入。\n",
    "        self.lstm = nn.LSTM(input_size, hidden_layer_size)\n",
    "        self.linear = nn.Linear(hidden_layer_size, output_size)\n",
    "\n",
    "        #初始化隐含状态及细胞状态C，hidden_cell变量包含先前的隐藏状态和单元状态\n",
    "        self.hidden_cell = (torch.zeros(1, 1, self.hidden_layer_size),\n",
    "                            torch.zeros(1, 1, self.hidden_layer_size))\n",
    "                            # 为什么的第二个参数也是1\n",
    "                            # 第二个参数代表的应该是batch_size吧\n",
    "                            # h_0(num_directions * num_layers, batch_size, hidden_size)\n",
    "                            # c_0(num_directions * num_layers, batch_size, hidden_size)\n",
    "                            \n",
    "\n",
    "    def forward(self, input_seq):\n",
    "        # print(input_seq.view(len(input_seq),1,-1).shape)\n",
    "        # 在时间序列输入时，时间序列的时间步对应的应为seq_len  3,时间点中的特征为input_size  4，此时输入应该是(3,1,4)\n",
    "        lstm_out, self.hidden_cell = self.lstm(input_seq.view(len(input_seq),1,-1), self.hidden_cell)  #  input(seq_len, batch_size, input_size)\n",
    "        #lstm的输出是当前时间步的隐藏状态ht和单元状态ct以及输出lstm_out\n",
    "        #按照lstm的格式修改input_seq的形状，作为linear层的输入\n",
    "        predictions = self.linear(lstm_out.view(len(input_seq), -1))\n",
    "        # print(predictions)\n",
    "        return predictions[-1]#返回predictions的最后一个元素\n",
    "\n",
    "# forward方法：LSTM层的输入与输出：out, (ht,Ct)=lstm(input,(h0,C0)),其中\n",
    "# 一、输入格式：lstm(input,(h0, C0))\n",
    "# 1、input为（seq_len,batch,input_size）格式的tensor,seq_len即为time_step\n",
    "# 2、h0为(num_layers * num_directions, batch, hidden_size)格式的tensor，隐藏状态的初始状态\n",
    "# 3、C0为(seq_len, batch, input_size）格式的tensor，细胞初始状态\n",
    "# 二、输出格式：output,(ht,Ct)\n",
    "# 1、output为(seq_len, batch, num_directions*hidden_size）格式的tensor，包含输出特征h_t(源于LSTM每个t的最后一层)\n",
    "# 2、ht为(num_layers * num_directions, batch, hidden_size)格式的tensor，\n",
    "# 3、Ct为(num_layers * num_directions, batch, hidden_size)格式的tensor，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LSTM1(\n",
      "  (lstm): LSTM(4, 100)\n",
      "  (linear): Linear(in_features=100, out_features=1, bias=True)\n",
      ")\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Anaconda3\\envs\\Pytorch\\lib\\site-packages\\torch\\nn\\modules\\loss.py:529: UserWarning: Using a target size (torch.Size([])) that is different to the input size (torch.Size([1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.\n",
      "  return F.mse_loss(input, target, reduction=self.reduction)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch:  1 loss:15861899.00000000\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_21056\\1388875615.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     25\u001b[0m         \u001b[1;31m#计算损失，反向传播梯度以及更新模型参数\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     26\u001b[0m         \u001b[0msingle_loss\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mloss_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0my_pred\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;31m#训练过程中，正向传播生成网络的输出，计算输出和实际值之间的损失值\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 27\u001b[1;33m         \u001b[0msingle_loss\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;31m#调用loss.backward()自动生成梯度，\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     28\u001b[0m         \u001b[0moptimizer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;31m#使用optimizer.step()执行优化器，把梯度传播回每个网络\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     29\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mC:\\Anaconda3\\envs\\Pytorch\\lib\\site-packages\\torch\\_tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[1;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[0;32m    361\u001b[0m                 \u001b[0mcreate_graph\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mcreate_graph\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    362\u001b[0m                 inputs=inputs)\n\u001b[1;32m--> 363\u001b[1;33m         \u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0minputs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    364\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    365\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mC:\\Anaconda3\\envs\\Pytorch\\lib\\site-packages\\torch\\autograd\\__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[1;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[0;32m    173\u001b[0m     Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass\n\u001b[0;32m    174\u001b[0m         \u001b[0mtensors\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mgrad_tensors_\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 175\u001b[1;33m         allow_unreachable=True, accumulate_grad=True)  # Calls into the C++ engine to run the backward pass\n\u001b[0m\u001b[0;32m    176\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    177\u001b[0m def grad(\n",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "#创建LSTM()类的对象，定义损失函数和优化器\n",
    "model = LSTM1()\n",
    "loss_function = nn.MSELoss()\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)#建立优化器实例\n",
    "print(model)\n",
    "\n",
    "\"\"\"\n",
    "模型训练\n",
    "batch-size是指1次迭代所使用的样本量；\n",
    "epoch是指把所有训练数据完整的过一遍；\n",
    "由于默认情况下权重是在PyTorch神经网络中随机初始化的，因此可能会获得不同的值。\n",
    "\"\"\"\n",
    "epochs = 150\n",
    "for i in range(epochs):\n",
    "    for seq, labels in input_seq:\n",
    "        # print(seq)\n",
    "        # print(labels)\n",
    "        #清除网络先前的梯度值\n",
    "        optimizer.zero_grad()#训练模型时需要使模型处于训练模式，即调用model.train()。缺省情况下梯度是累加的，需要手工把梯度初始化或者清零，调用optimizer.zero_grad()\n",
    "        #初始化隐藏层数据\n",
    "        model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),\n",
    "                             torch.zeros(1, 1, model.hidden_layer_size))\n",
    "        #实例化模型\n",
    "        y_pred = model(seq)\n",
    "        #计算损失，反向传播梯度以及更新模型参数\n",
    "        single_loss = loss_function(y_pred, labels)#训练过程中，正向传播生成网络的输出，计算输出和实际值之间的损失值\n",
    "        single_loss.backward()#调用loss.backward()自动生成梯度，\n",
    "        optimizer.step()#使用optimizer.step()执行优化器，把梯度传播回每个网络\n",
    "\n",
    "    # 查看模型训练的结果\n",
    "    if i%25 == 1:\n",
    "        print(f'epoch:{i:3} loss:{single_loss.item():10.8f}')\n",
    "print(f'epoch:{i:3} loss:{single_loss.item():10.10f}')"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "0a9e8949fc24b5f62ea486aa0e3668f251ab86c129ace0f639a1ad6db239a700"
  },
  "kernelspec": {
   "display_name": "Python 3.7.12 ('Pytorch')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.12"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
